Circuit explanation:
The power supply for the circuit is taken from the start battery, using a 78L05 voltage regulator.
The voltage measured is that from the comfortbattery or the combination comfort/start battery (bridge on). The
voltage is measured with the aid of a voltage divider constructed of R3/R4 to stay within a 5V limit. The maximum
input voltage is 15V (enough for the situation where the battery is fully charged and the sun shines).
When the voltage drops below 12,4 volts, the brigde will be put off. When the voltage rises above 13,5 volts, the
bridge will be put on. Between these two values the bridge state is left as-is (which can be either on or off,
depending from where we are coming from).
The operation of the circuit depends on the use of 2 opamps which compare the voltage against a preset voltage.
One opamp is used for the "low" treshold, the other one is used for the "high" treshold. The result will always be
0 or 1 (below or above treshold) and the two outputs from the opamps are connected to the input of a microcontroller
which will handle the control of the bridge programmatically.
The two opamps can be found in IC LM324 which has four of them, so that should be enough.
The low treshold and the high treshold should be set at around 12,4 and 13,5 volts resp. Adjustment can occur via POT1
and POT2.
The bridge itself consists of a so-called bi-stable relay which has two coils (one for off and one for on). This relay
has a coil voltage of 12V and a switching current of 12A at 12V.
The switching contacts of the bridge are not shown on the schematic diagram, you just should use the relays make-contact
and connect one end of it with the start battery, the other end goes to the comfort battery.
A bi-stable relay does not use any current no matter in which position it is (bridge on or bridge off). Only during
changing states you need a current for a short while (about 0,2 seconds pulse).
Both coils of the bi-stable relay are powered using a BC337 transistor, which as capable of doing this job.
Notice that the coils are in the 12V circuit, not in the 5V circuit.
Relay 3 in the schematic diagram is the "off" coil, Relay 2 is the "on" coil.
Relay 1, which is not strictly necessary, is used to disable an external alarm system while switching, for a short
while. This is done only because I have an alarm system that is triggered by sudden voltage changes. You can do
without Relay 1 if you don't need such an interface with your alarm system.
You can also operate the bridge manually using pushbutton SW1. This works only when the voltage is in the floating window,
that is between 12,4 and 13,5 volts.
Like mentioned before, the relay is a bi-stable relay. You should use a heavy model, for instance Conrad ordering nbr.
504173 - 62.
This one has a switching capability of 16 amps. The software takes care of the pulse operation.
If you can find a bi-stable relay that has an even higher switching capability of say 30 amps (which I couldn't), you
might use the circuit for making the bridge while driving. In that case however, a software adjustment is necessary.
Two LEDs (with current limiting resistor) can come in handy when connected to both coils of the bi-stable relay (this
is not shown in the diagram). Then you can see when bridging/de-bridging occurs (LED whill shortly light).
;;
;; ;; Zonnebatterij automaat, a PIC 16F84 program to switch two lead-acid batteries in parallel ;; when there is enough sunlight. ;; ;; Copyright (C) 2000 Geert Van Espen (geert@vanespen.com) ;; ;; LIST P=16F84 errorlevel 0,-305 processor 16f84 include "p16f84.inc" __CONFIG _CP_OFF & _RC_OSC & _PWRTE_ON & _WDT_ON ; ; ; ; ; commando om dit project naar de pic te sturen: ; TOPIC -RWG SOLARSW.HEX ; ; ; ; ; ; current settings: low level = 12.29 V ; high level = 13.45 V ; ; general purpose register allocation werk equ 0x0c ; werkregister t1 equ 0x0d ; telwerk 1 t2 equ 0x0e ; telwerk 2 TIJDl equ 0x10 ; to keep track of time TIJDh equ 0x11 ddd equ 0x13 ; debug only KLAPPERTIJD0 equ 0x14 ; hysteresis 0 KLAPPERTIJD1 equ 0x15 ; hysteresis 1 KLAPPERTIJD2 equ 0x16 ; hysteresis 2 KLAPPERTIJD3 equ 0x17 ; hysteresis 3 RB0_STAND equ 0x18 ; 0=uit 1=aan RB1_STAND equ 0x19 ; 0=uit 1=aan RB2_STAND equ 0x1a ; 0=uit 1=aan RB3_STAND equ 0x1b ; 0=uit 1=aan tempvar equ 0x1c ; Wl equ 0x20 ; Wh equ 0x21 ; BRUG equ 0x22 ; 0 = geen doorkoppeling batterijen ; 1 = wel doorkoppeling batterijen CONTACT equ 0x23 ; 0 = contactsleutel uit ; 1 = contactsleutel aan PrevRB0 equ 0x24 ; vorige RB0 situatie PrevRB1 equ 0x25 ; vorige RB1 situatie PrevRB2 equ 0x26 ; vorige contact situatie PrevBtn equ 0x27 ; vorige pushbutton situatie ; tijdconstanten ;;;;;;;;;;;;;;;; m_input_a equ b'00000000' ; input mask, which bits are input m_input_b equ b'11111111' ; org 0x00 ; reset vector goto main dt "2000-10-10 geert@vanespen.com Zonnebatterij automaat" ;*********************************************************************** ;* * ;* initialise * ;* * ;*********************************************************************** initialise: ; set up ports MOVLW 0 ; TRIS 5 ; MOVLW 0x0F ; TRIS 6 ; MOVLW 0x80 ; OPTION ; clrf TIJDl ; initialisaties clrf TIJDh ; clrf KLAPPERTIJD0 ; clrf KLAPPERTIJD1 ; clrf KLAPPERTIJD2 ; clrf KLAPPERTIJD3 ; clrf RB0_STAND ; clrf RB1_STAND ; clrf RB2_STAND ; clrf RB3_STAND ; clrf BRUG ; clrf CONTACT ; clrf PrevRB0 ; clrf PrevRB1 ; clrf PrevRB2 ; clrf PrevBtn ; clrf PORTA ; UIT laag clrf PORTB ; uitgang even aan en uit laten knipperen om te laten zien dat het ; apparaat werkt movlw 0xFE ; startwaarde movwf PORTA ; movlw 6 ; knipper 3 keer movwf t1 ; functiontest: movlw 0x19 ; wacht halve seconde call wachtlus ; movlw 0xFF ; xorwf PORTA, f ; decfsz t1, f ; goto functiontest ; clrf PORTA ; UIT laag retlw 0 ;*********************************************************************** ;* * ;* wachtlus * ;* * ;*********************************************************************** wachtlus: ; wacht x keer 18 milliseconden, met x in w movwf werk ; zet w in werk ; debug only ; movlw 1 ; movwf werk slapertje: sleep ; decfsz werk, f ; goto slapertje ; retlw 0 ; ;*********************************************************************** ;* * ;* test_portRB0 * ;* * ;*********************************************************************** ; hysteresis voor poort RB0: als RB0 een tijdje 0 is wordt RB0_STAND ; ook 0; als RB0 1 is wordt RB0_STAND onmiddellijk 1 test_portRB0: movf PORTB, w ; w = PORTB andlw 1 ; we willen alleen RB0 movwf tempvar ; movf tempvar, w ; btfss tempvar, 0 ; goto test0_low ; movlw 1 ; movwf RB0_STAND ; clrf KLAPPERTIJD0 ; KLAPPERTIJD0 = 0 retlw 0 ; test0_low: subwf RB0_STAND, w ; btfss STATUS, Z ; RB0 input = RB0_STAND? goto test0_hyst1 ; clrf KLAPPERTIJD0 ; ja: KLAPPERTIJD0 = 0 goto test0_hyst2 ; test0_hyst1: incf KLAPPERTIJD0, f ; neen: verhoog KLAPPERTIJD0 test0_hyst2: btfss KLAPPERTIJD0, 2 ; KLAPPERTIJD0 hoog? goto test0_hyst3 ; neen: gedaan movf tempvar, w ; movwf RB0_STAND ; ja: RB0_STAND = RB0 input clrf KLAPPERTIJD0 ; KLAPPERTIJD0 = 0 test0_hyst3: retlw 0 ; ;*********************************************************************** ;* * ;* test_portRB1 * ;* * ;*********************************************************************** ; hysteresis voor poort RB1: als RB1 een tijdje 0 is wordt RB1_STAND ; ook 0; als RB1 1 is wordt RB1_STAND onmiddellijk 1 test_portRB1: movf PORTB, w ; w = PORTB andlw 2 ; we willen alleen RB1 movwf tempvar ; bcf STATUS, C ; rrf tempvar, f ; movf tempvar, w ; btfss tempvar, 0 ; goto test1_low ; movlw 1 ; movwf RB1_STAND ; clrf KLAPPERTIJD1 ; KLAPPERTIJD1 = 0 retlw 0 ; test1_low: subwf RB1_STAND, w ; btfss STATUS, Z ; RB1 input = RB1_STAND? goto test1_hyst1 ; clrf KLAPPERTIJD1 ; ja: KLAPPERTIJD1 = 0 goto test1_hyst2 ; test1_hyst1: incf KLAPPERTIJD1, f ; neen: verhoog KLAPPERTIJD1 test1_hyst2: btfss KLAPPERTIJD1, 2 ; KLAPPERTIJD1 hoog? goto test1_hyst3 ; neen: gedaan movf tempvar, w ; movwf RB1_STAND ; ja: RB1_STAND = RB1 input clrf KLAPPERTIJD1 ; KLAPPERTIJD1 = 0 test1_hyst3: retlw 0 ; ;*********************************************************************** ;* * ;* test_portRB2 * ;* * ;*********************************************************************** ; hysteresis voor poort RB2: als RB2 een tijdje 1 is wordt RB2_STAND ; ook 1; als RB2 een tijdje nul is wordt RB2_STAND ook nul test_portRB2: movf PORTB, w ; w = PORTB andlw 0x04 ; we willen alleen RB2 movwf tempvar ; bcf STATUS, C ; rrf tempvar, f ; rrf tempvar, f ; movf tempvar, w ; subwf RB2_STAND, w ; btfss STATUS, Z ; RB2 input = RB2_STAND? goto test2_hyst1 ; clrf KLAPPERTIJD2 ; ja: KLAPPERTIJD2 = 0 goto test2_hyst2 ; test2_hyst1: incf KLAPPERTIJD2, f ; neen: verhoog KLAPPERTIJD2 test2_hyst2: btfss KLAPPERTIJD2, 1 ; KLAPPERTIJD2 hoog? goto test2_hyst3 ; neen: gedaan movf tempvar, w ; movwf RB2_STAND ; ja: RB2_STAND = RB2 input clrf KLAPPERTIJD2 ; KLAPPERTIJD2 = 0 test2_hyst3: retlw 0 ; ;*********************************************************************** ;* * ;* test_portRB3 * ;* * ;*********************************************************************** ; hysteresis voor poort RB3: als RB3 een tijdje 1 is wordt RB3_STAND ; ook 1; als RB3 een tijdje nul is wordt RB3_STAND ook nul test_portRB3: movf PORTB, w ; w = PORTB andlw 0x08 ; we willen alleen RB3 movwf tempvar ; bcf STATUS, C ; rrf tempvar, f ; rrf tempvar, f ; rrf tempvar, f ; movf tempvar, w ; subwf RB3_STAND, w ; btfss STATUS, Z ; RB3 input = RB3_STAND? goto test3_hyst1 ; clrf KLAPPERTIJD3 ; ja: KLAPPERTIJD3 = 0 goto test3_hyst2 ; test3_hyst1: incf KLAPPERTIJD3, f ; neen: verhoog KLAPPERTIJD3 test3_hyst2: btfss KLAPPERTIJD3, 2 ; KLAPPERTIJD3 hoog? goto test3_hyst3 ; neen: gedaan movf tempvar, w ; movwf RB3_STAND ; ja: RB3_STAND = RB3 input clrf KLAPPERTIJD3 ; KLAPPERTIJD3 = 0 test3_hyst3: retlw 0 ; ;*********************************************************************** ;*********************************************************************** ;* * ;* programma * ;* * ;*********************************************************************** main: btfsc STATUS, NOT_TO ; if this is a reset or power-up ... call initialise ; set port drivers and WDT prescaler movlw 1 ; veronderstel contact aan movwf CONTACT ; movlw 3 ; movwf PrevRB0 ; movwf PrevRB1 ; movwf PrevRB2 ; movwf PrevBtn ; start: main_loop: ; ; LAGE DREMPELSPANNING (poort RB0) ; call test_portRB0 ; test spanning op poort RB0 movf PrevRB0, w ; test of RB0_STAND veranderd is subwf RB0_STAND, w ; ;;;;; btfsc STATUS, Z ; is de stand veranderd? ;;;;; goto test0_af ; ; veranderd movf RB0_STAND, w ; ja, de stand is veranderd movwf PrevRB0 ; btfsc RB0_STAND, 0 ; is spanning laag? goto test0_af ; neen, gedaan btfss BRUG, 0 ; ja: test of brug al af stond goto test0_af ; brug af: gedaan bsf PORTA, 2 ; spanning laag: alarm-disable relais bsf PORTA, 0 ; en RA0 een tijdje aanzetten movlw 0xc ; wacht fractie seconde call wachtlus ; clrf BRUG ; bewaar huidige toestand bcf PORTA, 0 ; poorten terug uitzetten bcf PORTA, 2 ; movlw 0x6f ; wacht 2 seconden call wachtlus ; test0_af: ; ; HOGE DREMPELSPANNING (poort RB1) ; btfsc CONTACT, 0 ; als contact aan, goto test1_af ; dan nooit brug maken! call test_portRB1 ; test spanning op poort RB1 movf PrevRB1, w ; subwf RB1_STAND, w ; ;;;;; btfsc STATUS, Z ; is de stand veranderd? ;;;;; goto test1_af ; ; veranderd movf RB1_STAND, w ; ja, de stand is veranderd movwf PrevRB1 ; btfss RB1_STAND, 0 ; is spanning hoog? goto test1_af ; neen, gedaan btfsc BRUG, 0 ; ja: test of brug al aan stond goto test1_af ; brug aan: gedaan bsf PORTA, 2 ; spanning hoog: alarm-disable relais bsf PORTA, 1 ; en RA1 een tijdje aanzetten movlw 0xc ; wacht fractie seconde call wachtlus ; bsf BRUG, 0 ; bewaar huidige toestand bcf PORTA, 1 ; poorten terug uitzetten bcf PORTA, 2 ; movlw 0x6f ; wacht 2 seconden call wachtlus ; test1_af: ; ; CONTACTSLEUTEL (poort RB2) ; ContactSleutel: call test_portRB2 ; test spanning op poort RB2 movf PrevRB2, w ; subwf RB2_STAND, w ; btfsc STATUS, Z ; is de stand veranderd? goto test2_af ; ; veranderd movf RB2_STAND, w ; ja, de stand is veranderd movwf PrevRB2 ; btfsc RB2_STAND, 0 ; is contact aan? goto test2_aan ; clrf CONTACT ; neen: bewaar huidige toestand bsf PrevRB0, 3 ; herinitialiseer spanningstesten bsf PrevRB1, 3 ; goto test2_af ; test2_aan: bsf CONTACT, 0 ; bewaar huidige toestand bsf PORTA, 2 ; alarm-disable relais en bsf PORTA, 0 ; RA0 een tijdje aanzetten movlw 0xc ; wacht fractie seconde call wachtlus ; clrf BRUG ; bewaar huidige toestand bcf PORTA, 0 ; poorten terug uitzetten bcf PORTA, 2 ; movlw 0xfe ; wacht 5 seconden call wachtlus ; test2_af: ; ; PUSHBUTTON (poort RB3) ; btfss CONTACT, 0 ; als contact aan, goto PushButton ; dan nooit brug maken! clrf PrevBtn ; goto PushButton_samen; PushButton: call test_portRB3 ; als pushbutton gedrukt is, movf PrevBtn, w ; dan toggelen subwf RB3_STAND, w ; btfsc STATUS, Z ; is de stand veranderd? goto PushButton_gelijk; PushButton_changed: movf RB3_STAND, w ; ja: PrevBtn = RB3_STAND movwf PrevBtn ; btfss RB3_STAND, 0 ; is pushbutton gedrukt? goto PushButton_af ; movlw 1 ; ja: toggle BRUG xorwf BRUG, f ; ; reflect BRUG value onto output ports btfss BRUG, 0 ; BRUG waarde testen goto PushButton_Reflect_uit; PushButton_Reflect_aan: bsf PORTA, 2 ; alarm-disable relais en bsf PORTA, 1 ; RA1 een tijdje aanzetten movlw 0xc ; wacht fractie seconde call wachtlus ; bcf PORTA, 1 ; poorten terug uitzetten bcf PORTA, 2 ; goto PushButton_samen; PushButton_Reflect_uit: bsf PORTA, 2 ; alarm-disable relais en bsf PORTA, 0 ; RA0 een tijdje aanzetten movlw 0xc ; wacht fractie seconde call wachtlus ; bcf PORTA, 0 ; poorten terug uitzetten bcf PORTA, 2 ; goto PushButton_samen; PushButton_samen: PushButton_gelijk: goto PushButton_af ; PushButton_af: movlw 0x2 ; wacht fractie seconde call wachtlus ; goto start ; ;; ;; end
Download source code (assembler) Download hex file (machine code)